package io.seata.samples.service.impl;

import java.util.Date;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import io.seata.core.context.RootContext;
import io.seata.core.exception.TransactionException;
import io.seata.samples.entity.Account;
import io.seata.samples.entity.Orders;
import io.seata.samples.entity.Product;
import io.seata.samples.service.DemoService;
import io.seata.samples.service.IAccountService;
import io.seata.samples.service.IOrdersService;
import io.seata.samples.service.IProductService;
import io.seata.spring.annotation.GlobalTransactional;
import io.seata.tm.api.GlobalTransactionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author 陈健斌
 * @date 2019/12/05
 */
@Service
public class DemoServiceImpl implements DemoService {
    private final static Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
    @Autowired
    private IAccountService accountService;
    @Autowired
    private IOrdersService ordersService;
    @Autowired
    private IProductService productService;
    private Lock lock = new ReentrantLock();

    /**
     * @return
     * @throws TransactionException
     */
    @Override
    @GlobalTransactional
    public Object testRollback() throws TransactionException {
        logger.info("seata分布式事务Id:{}", RootContext.getXID());
//        lock.lock();
        try {
            Date now = new Date();
            Product product = productService.getOne(Wrappers.<Product>query().eq("id", 1).last("for update"));
            if (product.getStock() > 0) {
                Account account = accountService.getById(1);
                Orders orders = new Orders();
                orders.setCreateTime(now);
                orders.setProductId(product.getId());
                orders.setReplaceTime(now);
                orders.setSum(1);
                orders.setAmount(product.getPrice());
                orders.setAccountId(account.getId());
                product.setStock(product.getStock() - 1);
                account.setSum(account.getSum() != null ? account.getSum() + 1 : 1);
                account.setLastUpdateTime(now);
                productService.updateById(product);
                accountService.updateById(account);
                ordersService.save(orders);
//                int i = 1 / 0;
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            // TODO: handle exception
            logger.info("载入事务id进行回滚");
            GlobalTransactionContext.reload(RootContext.getXID()).rollback();
            throw e;
        } finally {
//            lock.unlock();
        }
    }

    /**
     * //    @Transactional(rollbackFor = Exception.class)
     * //    @DS(value = "master_3") 这两个注解要结合起来一起用
     * 2、 一个JVM只能跑一个断点
     *3、 把本地锁lock注销掉
     *
     * @return
     * @throws TransactionException
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
//    @Transactional(rollbackFor = Exception.class)
//    @DS(value = "master_3")
    public Object testCommit() throws TransactionException {
//        lock.lock();
        try {
            logger.info("全局事务id:{}", RootContext.getXID());
            Date now = new Date();
          /*  Product product = productService.getOne(Wrappers.<Product>query().eq("id", 1).last("for update"));
            product.setStock(product.getStock() - 1);*/

            Product product = new Product();
            product.setId(1);
            product.setStock(100);
            productService.updateById(product);

            // TODO: 2021/10/19  模拟占用product:1本地锁
            Long wait = 0L;
            while (wait <= 6L) {
                try {
                    Thread.sleep(10 * 1000L);
                    wait++;
                    logger.info("wait = {}", wait);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            return true;
        } finally {
//            lock.unlock();
        }
    }
}
